"""
弹窗处理器

专门处理各种类型的弹窗
"""

import asyncio
import sys
from typing import Dict, Any, List, Optional
from playwright.async_api import Page
from pathlib import Path

# 添加项目根目录到Python路径
project_root = Path(__file__).parent.parent.parent.parent
sys.path.insert(0, str(project_root))

try:
    from src.browser.page_handler import BasePageHandler
    from src.utils.logger import get_logger
except ImportError:
    try:
        from ..page_handler import BasePageHandler
        from utils.logger import get_logger
    except ImportError:
        from browser.page_handler import BasePageHandler
        from utils.logger import get_logger

logger = get_logger(__name__)


class PopupHandler(BasePageHandler):
    """弹窗处理器类"""
    
    def __init__(self, page: Page):
        super().__init__(page)
        self.popup_types = {
            'swal2_popup': {
                'selectors': [
                    '.swal2-container',
                    'div.swal2-container.swal2-center.swal2-fade.swal2-shown',
                    '.swal2-popup'
                ],
                'close_methods': [
                    'body > div.swal2-container.swal2-center.swal2-fade.swal2-shown > div > div.swal2-header > button',
                    '/html/body/div[3]/div/div[1]/button',
                    '.swal2-close',
                    'button.swal2-close'
                ]
            },
            'advertisement': {
                'selectors': [
                    '.ad-popup', '.advertisement', '.ad-modal', '.ad-overlay',
                    '[id*="ad"]', '[class*="ad"]', '.banner-popup'
                ],
                'close_methods': [
                    'button:has-text("关闭")', 'button:has-text("×")', 
                    '.close', '.close-btn', '.ad-close'
                ]
            },
            'notification': {
                'selectors': [
                    '.notification', '.alert', '.toast', '.message-popup',
                    '.notification-popup', '.alert-popup'
                ],
                'close_methods': [
                    'button:has-text("确定")', 'button:has-text("知道了")',
                    '.close', '.dismiss'
                ]
            },
            'confirmation': {
                'selectors': [
                    '.confirm-dialog', '.confirmation-popup', '.warning-dialog',
                    '[role="alertdialog"]'
                ],
                'close_methods': [
                    'button:has-text("确定")', 'button:has-text("取消")',
                    'button:has-text("关闭")'
                ]
            },
            'modal': {
                'selectors': [
                    '.modal', '.dialog', '.popup', '.overlay',
                    '[role="dialog"]', '.modal-container'
                ],
                'close_methods': [
                    'button:has-text("关闭")', 'button:has-text("×")',
                    '.modal-close', '.dialog-close', '.close'
                ]
            },
            'cookie_consent': {
                'selectors': [
                    '.cookie-popup', '.cookie-banner', '.gdpr-popup',
                    '[id*="cookie"]', '[class*="cookie"]'
                ],
                'close_methods': [
                    'button:has-text("接受")', 'button:has-text("同意")',
                    'button:has-text("确定")', '.accept-cookies'
                ]
            }
        }
    
    async def handle(self) -> Dict[str, Any]:
        """
        处理页面中的所有弹窗
        
        Returns:
            Dict: 处理结果
        """
        try:
            self.logger.info("开始处理页面弹窗")
            
            # 第一步：等待页面完全加载
            await self._wait_for_page_fully_loaded()
            
            # 第二步：等待弹窗出现（弹窗通常在页面加载后才出现）
            await self._wait_for_popups_to_appear()
            
            # 第三步：检测所有弹窗
            popups = await self.detect_all_popups()
            
            # 第四步：关闭所有弹窗
            closed_count = 0
            failed_popups = []
            
            for popup in popups:
                try:
                    if await self.close_popup_by_type(popup):
                        closed_count += 1
                        self.logger.info(f"成功关闭{popup['type']}类型弹窗")
                    else:
                        failed_popups.append(popup)
                        self.logger.warning(f"无法关闭{popup['type']}类型弹窗")
                    
                    # 等待一下再处理下一个
                    await asyncio.sleep(0.5)
                    
                except Exception as e:
                    self.logger.error(f"关闭弹窗失败: {str(e)}")
                    failed_popups.append(popup)
            
            result = {
                'total_popups': len(popups),
                'closed_count': closed_count,
                'failed_count': len(failed_popups),
                'failed_popups': failed_popups,
                'success_rate': closed_count / len(popups) if popups else 1.0
            }
            
            self.logger.info(f"弹窗处理完成: 总计{len(popups)}个，成功关闭{closed_count}个")
            return result
            
        except Exception as e:
            self.logger.error(f"弹窗处理失败: {str(e)}")
            return {'error': str(e), 'total_popups': 0, 'closed_count': 0}
    
    async def _wait_for_page_fully_loaded(self) -> None:
        """
        等待页面完全加载
        """
        try:
            # 等待网络空闲
            await self.page.wait_for_load_state("networkidle", timeout=10000)
            
            # 额外等待，确保动态内容加载完成
            await asyncio.sleep(2)
            
            # 等待文档就绪
            await self.page.wait_for_function("document.readyState === 'complete'", timeout=5000)
            
            self.logger.debug("页面完全加载完成")
            
        except Exception as e:
            self.logger.warning(f"等待页面加载超时: {str(e)}")
    
    async def _wait_for_popups_to_appear(self) -> None:
        """
        等待弹窗出现（弹窗通常在页面加载后延迟出现）
        """
        try:
            # 等待一段时间让弹窗有机会出现
            await asyncio.sleep(3)
            
            # 检查是否有弹窗正在动画中
            for _ in range(10):  # 最多等待5秒
                # 检查是否有正在显示的弹窗动画
                has_animating_popup = await self.page.evaluate("""
                    () => {
                        const popups = document.querySelectorAll('.swal2-container, .modal, .popup, .dialog');
                        for (let popup of popups) {
                            const style = window.getComputedStyle(popup);
                            if (style.animationName !== 'none' || style.transition !== 'none') {
                                return true;
                            }
                        }
                        return false;
                    }
                """)
                
                if not has_animating_popup:
                    break
                    
                await asyncio.sleep(0.5)
            
            self.logger.debug("弹窗等待完成")
            
        except Exception as e:
            self.logger.warning(f"等待弹窗出现失败: {str(e)}")
    
    async def detect_all_popups(self) -> List[Dict[str, Any]]:
        """
        检测页面中的所有弹窗
        
        Returns:
            List: 弹窗信息列表
        """
        popups = []
        
        for popup_type, config in self.popup_types.items():
            for selector in config['selectors']:
                try:
                    # 特殊处理swal2弹窗
                    if popup_type == 'swal2_popup':
                        elements = await self._detect_swal2_popups()
                        for element in elements:
                            popup_info = await self._analyze_popup_element(element, popup_type)
                            if popup_info:
                                popups.append(popup_info)
                    else:
                        elements = await self.page.query_selector_all(selector)
                        for element in elements:
                            if await element.is_visible():
                                popup_info = await self._analyze_popup_element(element, popup_type)
                                if popup_info:
                                    popups.append(popup_info)
                                
                except Exception as e:
                    self.logger.debug(f"检测{popup_type}弹窗失败: {str(e)}")
        
        # 去重（基于位置）
        unique_popups = self._deduplicate_popups(popups)
        
        self.logger.info(f"检测到{len(unique_popups)}个唯一弹窗")
        return unique_popups
    
    async def _detect_swal2_popups(self) -> List:
        """
        专门检测swal2弹窗
        
        Returns:
            List: swal2弹窗元素列表
        """
        try:
            elements = []
            
            # 检测swal2容器
            swal2_selectors = [
                '.swal2-container.swal2-shown',
                'div.swal2-container.swal2-center.swal2-fade.swal2-shown',
                '.swal2-popup',
                '[class*="swal2-container"][class*="shown"]'
            ]
            
            for selector in swal2_selectors:
                try:
                    found_elements = await self.page.query_selector_all(selector)
                    for element in found_elements:
                        if await element.is_visible():
                            # 检查是否真的是弹窗
                            display = await element.evaluate('el => window.getComputedStyle(el).display')
                            visibility = await element.evaluate('el => window.getComputedStyle(el).visibility')
                            opacity = await element.evaluate('el => window.getComputedStyle(el).opacity')
                            
                            if display != 'none' and visibility != 'hidden' and float(opacity) > 0:
                                elements.append(element)
                                self.logger.info(f"检测到swal2弹窗: {selector}")
                                
                except Exception as e:
                    self.logger.debug(f"检测swal2弹窗失败 {selector}: {str(e)}")
            
            # 通过JavaScript检测
            try:
                js_detected = await self.page.evaluate("""
                    () => {
                        const elements = [];
                        
                        // 检查swal2实例
                        if (window.Swal && window.Swal.isVisible && window.Swal.isVisible()) {
                            const container = document.querySelector('.swal2-container');
                            if (container) {
                                elements.push(container);
                            }
                        }
                        
                        // 检查DOM中的swal2元素
                        const swal2Elements = document.querySelectorAll('.swal2-container, .swal2-popup');
                        swal2Elements.forEach(el => {
                            const style = window.getComputedStyle(el);
                            if (style.display !== 'none' && style.visibility !== 'hidden' && parseFloat(style.opacity) > 0) {
                                elements.push(el);
                            }
                        });
                        
                        return elements.length;
                    }
                """)
                
                if js_detected > 0:
                    self.logger.info(f"JavaScript检测到 {js_detected} 个swal2弹窗")
                    
            except Exception as e:
                self.logger.debug(f"JavaScript检测swal2失败: {str(e)}")
            
            return elements
            
        except Exception as e:
            self.logger.error(f"检测swal2弹窗失败: {str(e)}")
            return []
    
    async def _analyze_popup_element(self, element, popup_type: str) -> Optional[Dict[str, Any]]:
        """
        分析弹窗元素
        
        Args:
            element: 弹窗元素
            popup_type: 弹窗类型
            
        Returns:
            Dict: 弹窗信息
        """
        try:
            # 获取弹窗基本信息
            text_content = await element.text_content()
            bounding_box = await element.bounding_box()
            
            # 查找关闭按钮
            close_buttons = []
            for close_selector in self.popup_types[popup_type]['close_methods']:
                try:
                    buttons = await element.query_selector_all(close_selector)
                    for button in buttons:
                        if await button.is_visible():
                            close_buttons.append({
                                'selector': close_selector,
                                'element': button,
                                'text': await button.text_content()
                            })
                except Exception as e:
                    self.logger.debug(f"查找关闭按钮失败: {str(e)}")
            
            # 检查是否可能是重要弹窗（需要用户交互）
            is_important = await self._is_important_popup(element, text_content)
            
            popup_info = {
                'type': popup_type,
                'element': element,
                'text_content': text_content[:200] if text_content else "",
                'position': bounding_box,
                'close_buttons': close_buttons,
                'has_close_button': len(close_buttons) > 0,
                'is_important': is_important,
                'z_index': await element.evaluate('el => window.getComputedStyle(el).zIndex')
            }
            
            return popup_info
            
        except Exception as e:
            self.logger.debug(f"分析弹窗元素失败: {str(e)}")
            return None
    
    async def _is_important_popup(self, element, text_content: str) -> bool:
        """
        判断是否是重要弹窗（可能需要用户确认）
        
        Args:
            element: 弹窗元素
            text_content: 文本内容
            
        Returns:
            bool: 是否重要
        """
        try:
            # 重要弹窗的关键词
            important_keywords = [
                '确认', '警告', '错误', '失败', '成功', '完成',
                '删除', '保存', '提交', '登录', '注册',
                'confirm', 'warning', 'error', 'success', 'delete'
            ]
            
            if text_content:
                text_lower = text_content.lower()
                for keyword in important_keywords:
                    if keyword in text_lower:
                        return True
            
            # 检查是否有表单元素
            form_elements = await element.query_selector_all('input, select, textarea')
            if form_elements:
                return True
            
            # 检查是否有多个操作按钮
            buttons = await element.query_selector_all('button')
            if len(buttons) > 1:
                return True
            
            return False
            
        except Exception as e:
            self.logger.debug(f"判断弹窗重要性失败: {str(e)}")
            return False
    
    def _deduplicate_popups(self, popups: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
        """
        去除重复的弹窗（基于位置）
        
        Args:
            popups: 弹窗列表
            
        Returns:
            List: 去重后的弹窗列表
        """
        unique_popups = []
        
        for popup in popups:
            is_duplicate = False
            popup_pos = popup.get('position')
            
            if popup_pos:
                for existing in unique_popups:
                    existing_pos = existing.get('position')
                    if existing_pos:
                        # 如果位置相近，认为是重复的
                        if (abs(popup_pos['x'] - existing_pos['x']) < 10 and
                            abs(popup_pos['y'] - existing_pos['y']) < 10):
                            is_duplicate = True
                            break
            
            if not is_duplicate:
                unique_popups.append(popup)
        
        return unique_popups
    
    async def close_popup_by_type(self, popup_info: Dict[str, Any]) -> bool:
        """
        根据弹窗类型关闭弹窗
        
        Args:
            popup_info: 弹窗信息
            
        Returns:
            bool: 是否成功关闭
        """
        try:
            element = popup_info['element']
            popup_type = popup_info['type']
            
            # 如果是重要弹窗，记录日志但可能需要特殊处理
            if popup_info.get('is_important'):
                self.logger.warning(f"检测到重要弹窗: {popup_info['text_content'][:50]}")
            
            # 特殊处理swal2弹窗
            if popup_type == 'swal2_popup':
                return await self._close_swal2_popup(element)
            
            # 尝试使用找到的关闭按钮
            for close_button in popup_info['close_buttons']:
                try:
                    await close_button['element'].click()
                    self.logger.info(f"点击关闭按钮: {close_button['text']}")
                    
                    # 等待弹窗消失
                    await asyncio.sleep(0.5)
                    if not await element.is_visible():
                        return True
                        
                except Exception as e:
                    self.logger.debug(f"点击关闭按钮失败: {str(e)}")
            
            # 如果没有关闭按钮或点击失败，尝试其他方法
            return await self._try_alternative_close_methods(element, popup_type)
            
        except Exception as e:
            self.logger.error(f"关闭弹窗失败: {str(e)}")
            return False
    
    async def _close_swal2_popup(self, element) -> bool:
        """
        专门处理swal2弹窗的关闭
        
        Args:
            element: 弹窗元素
            
        Returns:
            bool: 是否成功关闭
        """
        try:
            # 方法1: 使用CSS选择器
            close_selectors = [
                'body > div.swal2-container.swal2-center.swal2-fade.swal2-shown > div > div.swal2-header > button',
                '.swal2-close',
                'button.swal2-close',
                '.swal2-container .swal2-close'
            ]
            
            for selector in close_selectors:
                try:
                    close_button = await self.page.query_selector(selector)
                    if close_button and await close_button.is_visible():
                        await close_button.click()
                        self.logger.info(f"通过CSS选择器关闭swal2弹窗: {selector}")
                        await asyncio.sleep(0.5)
                        if not await element.is_visible():
                            return True
                except Exception as e:
                    self.logger.debug(f"CSS选择器关闭失败: {str(e)}")
            
            # 方法2: 使用XPath
            xpath_selectors = [
                '/html/body/div[3]/div/div[1]/button',
                '//div[@class="swal2-header"]//button',
                '//button[@class="swal2-close"]'
            ]
            
            for xpath in xpath_selectors:
                try:
                    close_button = await self.page.query_selector(f'xpath={xpath}')
                    if close_button and await close_button.is_visible():
                        await close_button.click()
                        self.logger.info(f"通过XPath关闭swal2弹窗: {xpath}")
                        await asyncio.sleep(0.5)
                        if not await element.is_visible():
                            return True
                except Exception as e:
                    self.logger.debug(f"XPath选择器关闭失败: {str(e)}")
            
            # 方法3: 通过JavaScript直接关闭
            try:
                await self.page.evaluate("""
                    () => {
                        // 尝试调用swal2的关闭方法
                        if (window.Swal && window.Swal.close) {
                            window.Swal.close();
                            return true;
                        }
                        
                        // 尝试点击关闭按钮
                        const closeBtn = document.querySelector('.swal2-close');
                        if (closeBtn) {
                            closeBtn.click();
                            return true;
                        }
                        
                        // 尝试移除弹窗容器
                        const container = document.querySelector('.swal2-container');
                        if (container) {
                            container.remove();
                            return true;
                        }
                        
                        return false;
                    }
                """)
                
                await asyncio.sleep(0.5)
                if not await element.is_visible():
                    self.logger.info("通过JavaScript关闭swal2弹窗")
                    return True
                    
            except Exception as e:
                self.logger.debug(f"JavaScript关闭失败: {str(e)}")
            
            # 方法4: 按ESC键
            try:
                await self.page.keyboard.press("Escape")
                await asyncio.sleep(0.5)
                if not await element.is_visible():
                    self.logger.info("通过ESC键关闭swal2弹窗")
                    return True
            except Exception as e:
                self.logger.debug(f"ESC键关闭失败: {str(e)}")
            
            return False
            
        except Exception as e:
            self.logger.error(f"关闭swal2弹窗失败: {str(e)}")
            return False
    
    async def _try_alternative_close_methods(self, element, popup_type: str) -> bool:
        """
        尝试其他关闭方法
        
        Args:
            element: 弹窗元素
            popup_type: 弹窗类型
            
        Returns:
            bool: 是否成功关闭
        """
        try:
            # 方法1: 按ESC键
            try:
                await self.page.keyboard.press("Escape")
                await asyncio.sleep(0.5)
                if not await element.is_visible():
                    self.logger.info("通过ESC键关闭弹窗")
                    return True
            except Exception as e:
                self.logger.debug(f"ESC键关闭失败: {str(e)}")
            
            # 方法2: 点击遮罩层
            try:
                # 查找父级遮罩层
                overlay = await element.evaluate("""
                    el => {
                        let parent = el.parentElement;
                        while (parent) {
                            const style = window.getComputedStyle(parent);
                            if (style.position === 'fixed' && 
                                (style.backgroundColor.includes('rgba') || 
                                 style.backgroundColor === 'black' ||
                                 parent.classList.contains('overlay') ||
                                 parent.classList.contains('backdrop'))) {
                                return parent;
                            }
                            parent = parent.parentElement;
                        }
                        return null;
                    }
                """)
                
                if overlay:
                    # 点击遮罩层的边缘
                    box = await element.bounding_box()
                    if box:
                        # 点击弹窗外的区域
                        await self.page.mouse.click(box['x'] - 10, box['y'] - 10)
                        await asyncio.sleep(0.5)
                        if not await element.is_visible():
                            self.logger.info("通过点击遮罩层关闭弹窗")
                            return True
                            
            except Exception as e:
                self.logger.debug(f"点击遮罩层失败: {str(e)}")
            
            # 方法3: 查找任何可能的关闭元素
            try:
                close_selectors = [
                    'button', 'a', 'span', 'div', 'i'
                ]
                
                for selector in close_selectors:
                    elements = await element.query_selector_all(selector)
                    for elem in elements:
                        text = await elem.text_content()
                        if text and any(keyword in text.lower() for keyword in ['×', '关闭', 'close', '取消', 'cancel']):
                            await elem.click()
                            await asyncio.sleep(0.5)
                            if not await element.is_visible():
                                self.logger.info(f"通过文本匹配关闭弹窗: {text}")
                                return True
                                
            except Exception as e:
                self.logger.debug(f"文本匹配关闭失败: {str(e)}")
            
            return False
            
        except Exception as e:
            self.logger.error(f"尝试其他关闭方法失败: {str(e)}")
            return False
    
    async def handle_specific_popup_type(self, popup_type: str) -> Dict[str, Any]:
        """
        处理特定类型的弹窗
        
        Args:
            popup_type: 弹窗类型
            
        Returns:
            Dict: 处理结果
        """
        try:
            if popup_type not in self.popup_types:
                return {'error': f'未知的弹窗类型: {popup_type}'}
            
            config = self.popup_types[popup_type]
            found_popups = []
            
            # 查找指定类型的弹窗
            for selector in config['selectors']:
                try:
                    elements = await self.page.query_selector_all(selector)
                    for element in elements:
                        if await element.is_visible():
                            popup_info = await self._analyze_popup_element(element, popup_type, selector)
                            if popup_info:
                                found_popups.append(popup_info)
                except Exception as e:
                    self.logger.debug(f"查找{popup_type}弹窗失败: {str(e)}")
            
            # 关闭找到的弹窗
            closed_count = 0
            for popup in found_popups:
                if await self.close_popup_by_type(popup):
                    closed_count += 1
            
            return {
                'popup_type': popup_type,
                'found_count': len(found_popups),
                'closed_count': closed_count,
                'success_rate': closed_count / len(found_popups) if found_popups else 1.0
            }
            
        except Exception as e:
            self.logger.error(f"处理{popup_type}弹窗失败: {str(e)}")
            return {'error': str(e), 'popup_type': popup_type}